﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Secams.model;
using LumenWorks.Framework.IO.Csv;
using System.Xml;
using Secams.controller;
using Secams.utils;

namespace Secams.model
{
    /// <summary>
    /// This class contains a collection of scenarios that is used as template for the trials
    /// in the scenario sequence
    /// </summary>
    public class ScenariosCollection
    {
        protected List<string> _scenarioNames = new List<string>();
        protected Dictionary<string, Scenario> _scenarioMap = new Dictionary<string, Scenario>();

        protected List<ScenarioInstanceSequence> _scenarioSequences = new List<ScenarioInstanceSequence>();

        public List<ScenarioInstanceSequence> getScenarioSequences()
        {
            return _scenarioSequences;
        }

        public ScenarioInstanceSequence getScenarioSequence(int idx)
        {
            return _scenarioSequences[idx];
        }

        public ScenarioInstanceSequence getScenarioSequence(string name)
        {
            for (int i = 0; i < _scenarioSequences.Count; i++)
            {
                if (_scenarioSequences[i].name.Equals(name))
                {
                    return _scenarioSequences[i];
                }
            }
            return null;
        }

        /// <summary>
        /// Loads the collection of scenarios from an xml file
        /// <![CDATA[
        /// <ScenariosCollection>
        ///     <scenarios>
        ///         <scenario id="scenarioName1" file="filename" />
        ///         <scenario id="scenarioName2" file="filename" />
        ///     </scenarios>
        ///     <scenarioSeqs>
        ///         <scenarioSeq id="version 1" scenarios="scenarioName1,scenarioName1" />
        ///             <scenarioInstance ref="t1" mode="train" feedbackLevel="2" />
        ///             <scenarioInstance ref="t1" mode="transfer" feedbackLevel="0" />
        ///         <scenarioSeq id="version 2" scenarios="scenarioName1,scenarioName2" />
        ///     </scenarioSeqs>
        /// </ScenariosCollection>
        /// ]]>
        /// </summary>
        /// <param name="filename"></param>
        public void loadCollection(string xmlFilename)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(xmlFilename);
            XmlNodeList selection = xmlDoc.SelectNodes("/ScenariosCollection/scenarios/scenario");
            foreach (XmlNode scenarioNode in selection)
            {
                string scenarioId = getAttribute(scenarioNode, "id");
                string scenarioFilename = getAttribute(scenarioNode, "file");

                loadScenario(scenarioNode, scenarioId, AppContext.getInstance().getScenarioFilePath(scenarioFilename));
            }

            selection = xmlDoc.SelectNodes("/ScenariosCollection/scenarioSeqs/scenarioSeq");
            foreach (XmlNode scenarioSeqNode in selection)
            {
                string scenarioId = getAttribute(scenarioSeqNode, "id");

                ScenarioInstanceSequence scenarioSeq = processScenarioSeq(scenarioSeqNode, scenarioId);

                _scenarioSequences.Add(scenarioSeq);
            }
        }

        public void loadScenario(XmlNode scenarioNode, string scenarioName, string filename)
        {
            //string path = Directory.GetCurrentDirectory();
            CsvReader csvReader = new CsvReader(new StreamReader(filename), true);

            int fieldCount = csvReader.FieldCount;
            string[] headers = csvReader.GetFieldHeaders();
            List<NiEvent> events = new List<NiEvent>();

            while (csvReader.ReadNextRecord())
            {
                NiEvent evt = csvToEvent(headers, csvReader);

                events.Add(evt);
            }


            Scenario scenario = new Scenario(scenarioName, events);
            _scenarioNames.Add(scenarioName);
            _scenarioMap.Add(scenario.name, scenario);

            // Retrieve score
            foreach (XmlNode innerNode in scenarioNode.ChildNodes)
            {
                if (innerNode.NodeType == XmlNodeType.Element)
                {
                    if (innerNode.Name.Equals("scoring"))
                    {
                        XmlElement scoreEl = (XmlElement)innerNode;
                        scenario.scoring = new Scoring();

                        string defaultWeightHit = AppContext.getInstance().getProperty("score-hit", 2).ToString();
                        string defaultWeightMiss = AppContext.getInstance().getProperty("score-miss", -2).ToString();
                        string defaultWeightFa = AppContext.getInstance().getProperty("score-fa", -1).ToString();
                        string defaultWeightCr = AppContext.getInstance().getProperty("score-cr", 1).ToString();

                        string scoreStr = getAttribute(scoreEl, "hit", defaultWeightHit);
                        scenario.scoring.pointHit = int.Parse(scoreStr);
                        scoreStr = getAttribute(scoreEl, "miss", defaultWeightMiss);
                        scenario.scoring.pointMiss = int.Parse(scoreStr);
                        scoreStr = getAttribute(scoreEl, "fa", defaultWeightFa);
                        scenario.scoring.pointFa = int.Parse(scoreStr);
                        scoreStr = getAttribute(scoreEl, "cr", defaultWeightCr);
                        scenario.scoring.pointCr = int.Parse(scoreStr);

                    }
                }
            }
        }

        public ScenarioInstanceSequence processScenarioSeq(XmlNode scenarioSeqNode, string scenarioSeqId)
        {
            ScenarioInstanceSequence retval = new ScenarioInstanceSequence(scenarioSeqId);

            foreach (XmlNode scenInstanceNode in scenarioSeqNode.ChildNodes)
            {
                if (scenInstanceNode.NodeType == XmlNodeType.Element)
                {
                    XmlElement scenInstanceEl = (XmlElement)scenInstanceNode;
                    if (scenInstanceEl.Name.Equals("scenarioInstance"))
                    {
                        string attrMode = getAttribute(scenInstanceEl, "mode");
                        string attrScenarioRef = getAttribute(scenInstanceEl, "ref");
                        int feebackLevel = int.Parse(getAttribute(scenInstanceEl, "feedbackLevel", "1"));
                        int delay = int.Parse(getAttribute(scenInstanceEl, "delayBetweenEvents", "2"));

                        int mode = TrialProfile.MODE_TRANSFER;

                        if (attrMode.ToLower().Equals("train"))
                            mode = TrialProfile.MODE_TRAINING;

                        if (!_scenarioMap.ContainsKey(attrScenarioRef))
                            throw new Exception("Scenario '" + attrScenarioRef + "' not found.");
                        retval.addScenario(_scenarioMap[attrScenarioRef], mode, feebackLevel, delay);
                    }
                }
            }
            return retval;
        }

        string getAttribute(XmlNode node, string attribName)
        {
            if (attribName == null) return null;
            XmlAttribute attrib = node.Attributes[attribName];
            if (attrib != null)
                return node.Attributes[attribName].Value;
            else return null;
        }

        string getAttribute(XmlNode node, string attribName, string defVal)
        {
            string retval = getAttribute(node, attribName);
            if (retval == null) retval = defVal;
            return retval;
        }

        protected NiEvent csvToEvent(string[] headers, CsvReader csvReader)
        {
            NiEvent retval = new NiEvent();
            CsvUtils.loadObject(headers, csvReader, retval);

            return retval;
        }

        /*
        protected NiEvent csvToEvent(CsvReader csvReader)
        {
            NiEvent retval = new NiEvent();

            retval.id = csvReader[0];
            retval.correlId = csvReader[1];
            retval.type = csvReader[2];
            retval.timestamp = DateTime.Now;
            //retval.sourceAddr = csvReader[0];
            retval.destAddr = csvReader[3];
            retval.port = (csvReader[4].Trim().Length > 0) ? int.Parse(csvReader[4]) : -1;
            retval.path = csvReader[5];
            retval.operation = csvReader[6];
            retval.riskLevel = (csvReader[7].Trim().Length > 0) ? int.Parse(csvReader[7]) : 0;
            //retval.protocol = csvReader[0];
            retval.isThreat = (csvReader[8].Trim().Equals("1")) ? true : false;
            retval.signature = csvReader[9];
            //retval.data = csvReader[9];

            return retval;
        }*/

    }
}
